package com.yifeng.repo.base.utils.http;

import com.yifeng.repo.base.utils.common.BaseUtil;

import javax.net.ssl.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FireHttpUtils的来源：
 * 1、主体从Openx-utils的HttpUtils拷贝
 * 2、扩展支持PUT，DELETE
 * 3、扩展支持Request header和Response header
 * @deprecated ：兼容代码，建议使用 com.yifeng.repo.base.utils.common.FireHttpUtils
 */
public class HttpExtUtils {
    private static class TrustAnyTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    public static final String RESPONSE_BODY = "Response-Body";
    private final String url;
    private final int timeout;
    private final boolean keepAlive;

    public HttpExtUtils(String url, int timeout) {
        this.url = url;
        this.timeout = timeout;
        this.keepAlive = false;
    }

    public HttpExtUtils(String url, int timeout, boolean keepAlive) {
        this.url = url;
        this.timeout = timeout;
        this.keepAlive = keepAlive;
    }

    public Map<String, String> get() throws IOException {
        return this.handle("GET", null, null);
    }

    public Map<String, String> get(Map<String, String> header) throws IOException {
        return this.handle("GET", header, null);
    }

    public Map<String, String> post(Map<String, String> header, String body) throws IOException {
        return this.handle("POST", header, body.getBytes(StandardCharsets.UTF_8));
    }

    public Map<String, String> post(Map<String, String> header, byte[] bodyBytes) throws IOException {
        return this.handle("POST", header, bodyBytes);
    }

    public Map<String, String> put(Map<String, String> header, String body) throws IOException {
        return this.handle("PUT", header, body.getBytes(StandardCharsets.UTF_8));
    }

    public Map<String, String> put(Map<String, String> header, byte[] bodyBytes) throws IOException {
        return this.handle("PUT", header, bodyBytes);
    }

    public Map<String, String> delete(Map<String, String> header, String body) throws IOException {
        return this.handle("DELETE", header, body.getBytes(StandardCharsets.UTF_8));
    }

    public Map<String, String> delete(Map<String, String> header, byte[] bodyBytes) throws IOException {
        return this.handle("DELETE", header, bodyBytes);
    }

    public String head(String header) throws IOException {
        URL url2 = new URL(url);
        HttpURLConnection conn = isHttps(url2) ? getHttpsConn(url2) : (HttpURLConnection) url2.openConnection();

        conn.setRequestMethod("HEAD");
        conn.setReadTimeout(timeout);
        conn.setConnectTimeout(timeout);
        if (conn.getResponseCode() != 200) {
            throw new IOException("Http response code error! " + conn.getResponseCode());
        } else {
            Map<String, List<String>> hs = conn.getHeaderFields();
            List<String> list = hs.get(header);
            return list != null && !list.isEmpty() ? list.get(0) : null;
        }
    }

    private Map<String, String> handle(String method, Map<String, String> header, byte[] bodyBytes) throws IOException {
        URL url2 = new URL(url);
        HttpURLConnection conn = isHttps(url2) ? getHttpsConn(url2) : (HttpURLConnection) url2.openConnection();

        // socket settings
        conn.setRequestMethod(method);
        if (!"GET".equalsIgnoreCase(method)) {
            conn.setDoOutput(true);
        }
        conn.setDoInput(true);
        conn.setReadTimeout(timeout);
        conn.setConnectTimeout(timeout);
        conn.setUseCaches(false);
        if (keepAlive) {
            conn.setRequestProperty("Connection", "Keep-Alive");
        } else {
            conn.setRequestProperty("Connection", "Close");
        }

        // write header
        if (header != null) {
            for (Map.Entry<String, String> entry : header.entrySet()) {
                conn.setRequestProperty(entry.getKey(), entry.getValue());
            }
        }

        // write body
        if (bodyBytes != null && bodyBytes.length != 0) {
            if (BaseUtil.isBlank(conn.getRequestProperty("Content-Type"))) {
                conn.setRequestProperty("Content-Type", "application/json; charset=" + StandardCharsets.UTF_8.name());
            }
            conn.setRequestProperty("Content-Length", String.valueOf(bodyBytes.length));
            conn.getOutputStream().write(bodyBytes);
        }

        int status = conn.getResponseCode();
        if (status >= 200 && status < 400) {
            Map<String, String> result = this.transferHeader(conn.getHeaderFields());
            String body = this.doRead(conn.getInputStream());
            result.put(RESPONSE_BODY, body);
            return result;
        } else {
            throw new IOException(this.doRead(conn.getErrorStream()));
        }
    }

    private HttpsURLConnection getHttpsConn(URL url) {
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new SecureRandom());
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, String> transferHeader(Map<String,List<String>> headerFields) {
        Map<String, String> header = new HashMap<>();
        for (Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
            if (entry.getKey() == null || entry.getKey().isEmpty()) {
                continue;
            }
            if (entry.getValue() == null || entry.getValue().isEmpty()) {
                continue;
            }
            header.put(entry.getKey(), entry.getValue().get(0));
        }
        return header;
    }

    private String doRead(InputStream is) throws IOException {
        byte[] buf = new byte[1024];
        ByteArrayOutputStream baos = new ByteArrayOutputStream(4 * 1024);
        for (int num; (num = is.read(buf)) != -1; ) {
            baos.write(buf, 0, num);
        }
        return baos.toString(StandardCharsets.UTF_8.name());
    }

    private boolean isHttps(URL url) {
        return "https".equalsIgnoreCase(url.getProtocol());
    }

}